<template>
    <div
        class="upgrade"
        v-draggable-resizable="{
            storageKey: 'upgrade',
            controlPosition: 'upgrade',
            hideHintText: '升级特效',
            scaleStep: 0.1,
            top: '15%',
            left: '40%',
            initSize: 0.8
        }">
        <div class="box">
            <span 
                v-show="showOverlay" 
                class="player-name"
                :style="playerNameStyle">{{ currentPlayerName }}</span>
            <canvas
                v-if="showCanvas"
                ref="upgradeCanvasRef"
                class="upgrade-gif"></canvas>
            <img
                v-show="showOverlay" 
                class="default-avatar"
                :src="currentAvatarSrc"
                :style="avatarStyle"
                alt="">
            <div
                v-show="showOverlay"
                class="upgrade-png"
                :style="upgradePngStyle">{{ currentLevelDisplay }}</div>
            <span 
                v-show="showOverlay" 
                class="player-rank">{{ playerRankText }}</span>
            <span 
                v-show="showOverlay && currentUpgradeText" 
                class="upgrade-txt">{{ currentUpgradeText }}</span>
        </div>
    </div>
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, defineProps, watch } from 'vue';
import eventBus from 'vue3-eventbus';
import defaultAvatar from '@/assets/xinJi/images/leaderboard/avatar.png';
import getApi from '@/api/modules/get';
import { sendMessage } from '@/utils/socketService';
import { getRank } from '@/utils/playerBox';

const HOLD_AFTER_FINAL_FRAME = 5000;
const OVERLAY_TRIGGER_FRAME = 38;
const LEVEL_ANIMATION_DURATION = 800;

type UpgradeEventItem = {
    playerName: string;
    avatar?: string;
    newLevel: number;
    oldLevel: number;
    rank?: number;
    effectLevel?: number;
};

type PlayerSeasonEventPayload = {
    player: Record<string, any>;
    seasonPoints?: number;
};

type UpgradeEffectConfig = {
    minLevel: number;
    effectIndex: number;
    top: string;
};

const props = defineProps({
    gameId: Number,
    platformId: String,
    upgradeSwitch: {
        type: Boolean,
        default: true
    }
});

const upgradeCanvasRef = ref<HTMLCanvasElement | null>(null);
const showCanvas = ref(false);
const showOverlay = ref(false);
const upgradeQueue = ref<UpgradeEventItem[]>([]);
const currentPlayer = ref<UpgradeEventItem | null>(null);
const currentEffectIndex = ref(1);
const animatedLevel = ref<number | null>(null);
const pendingLevelAnimation = ref<{ from: number; to: number } | null>(null);

const resolveUpgradeAssetUrl = (index: number, ext: 'gif' | 'png') =>
    new URL(`../../assets/xinJi/images/upgrade/${index}.${ext}`, import.meta.url).href;

const currentUpgradePng = computed(() => resolveUpgradeAssetUrl(currentEffectIndex.value, 'png'));
const currentUpgradeGif = computed(() => resolveUpgradeAssetUrl(currentEffectIndex.value, 'gif'));
const currentEffectTop = ref('145px');
const isHighTierEffect = computed(() => (currentPlayer.value?.newLevel ?? 0) >= 80);
const isTopShifted = computed(() => (currentPlayer.value?.newLevel ?? 0) >= 96);
const upgradePngStyle = computed(() => {
    const baseTop = parseFloat(currentEffectTop.value) || 145;
    const adjustedTop = baseTop + (isTopShifted.value ? 10 : 0);
    return {
        backgroundImage: `url(${currentUpgradePng.value})`,
        top: `${adjustedTop}px`,
        left: isHighTierEffect.value ? '125px' : '121px'
    };
});
const playerNameStyle = computed(() => (
    isHighTierEffect.value ? { top: '-2%' } : {}
));
const avatarStyle = computed(() => (
    isHighTierEffect.value ? { left: '110px' } : {}
));
const currentAvatarSrc = computed(() => currentPlayer.value?.avatar || defaultAvatar);
const currentPlayerName = computed(() => currentPlayer.value?.playerName ?? '');
const currentLevelDisplay = computed(() => {
    if (animatedLevel.value !== null) {
        return String(animatedLevel.value);
    }
    if (currentPlayer.value) {
        return String(currentPlayer.value.newLevel);
    }
    return '';
});
const currentUpgradeText = computed(() => {
    if (!currentPlayer.value) {
        return '';
    }
    const lookupLevel = resolveAwardLookupLevel(currentPlayer.value.newLevel);
    if (!lookupLevel) {
        return '';
    }
    const award = findUpgradeAwardByLevel(lookupLevel);
    if (!award) {
        return '';
    }
    return award.upgrade_txt || '';
});
const playerRankText = computed(() => {
    const rank = currentPlayer.value?.rank;
    return rank === undefined || rank === null ? '世界排名：-' : `世界排名：${rank}`;
});

const effectConfigs: UpgradeEffectConfig[] = [
    { minLevel: 96, effectIndex: 7, top: '120px' },
    { minLevel: 80, effectIndex: 6, top: '128px' },
    { minLevel: 64, effectIndex: 5, top: '145px' },
    { minLevel: 48, effectIndex: 4, top: '145px' },
    { minLevel: 32, effectIndex: 3, top: '145px' },
    { minLevel: 16, effectIndex: 2, top: '145px' },
    { minLevel: 1, effectIndex: 1, top: '145px' },
];

const getUpgradeEffectConfigByLevel = (level: number) => {
    for (const config of effectConfigs) {
        if (level >= config.minLevel) return config;
    }
    return effectConfigs[effectConfigs.length - 1];
};

let decoder: ImageDecoder | null = null;
let frameTimer: ReturnType<typeof setTimeout> | null = null;
let holdTimer: ReturnType<typeof setTimeout> | null = null;
let renderToken = 0;
let levelAnimationFrame: number | null = null;

const clearFrameTimer = () => {
    if (frameTimer) {
        clearTimeout(frameTimer);
        frameTimer = null;
    }
};

const clearHoldTimer = () => {
    if (holdTimer) {
        clearTimeout(holdTimer);
        holdTimer = null;
    }
};

const disposeDecoder = () => {
    clearFrameTimer();
    if (decoder) {
        decoder.close();
        decoder = null;
    }
};

const stopLevelAnimation = () => {
    if (levelAnimationFrame !== null && typeof window !== 'undefined') {
        window.cancelAnimationFrame(levelAnimationFrame);
    }
    levelAnimationFrame = null;
};

const animateLevelTransition = (from: number, to: number) => {
    stopLevelAnimation();
    if (typeof window === 'undefined' || from === to) {
        animatedLevel.value = to;
        return;
    }
    const start = window.performance.now();
    const step = (now: number) => {
        const progress = Math.min((now - start) / LEVEL_ANIMATION_DURATION, 1);
        const value = Math.round(from + (to - from) * progress);
        animatedLevel.value = value;
        if (progress < 1) {
            levelAnimationFrame = window.requestAnimationFrame(step);
        } else {
            levelAnimationFrame = null;
        }
    };
    levelAnimationFrame = window.requestAnimationFrame(step);
};

const startPendingLevelAnimation = (withDelay = false) => {
    if (!pendingLevelAnimation.value) {
        return;
    }
    const { from, to } = pendingLevelAnimation.value;
    pendingLevelAnimation.value = null;
    if (withDelay && typeof window !== 'undefined') {
        window.setTimeout(() => animateLevelTransition(from, to), 800);
    } else {
        animateLevelTransition(from, to);
    }
};

const handleUpgradeComplete = () => {
    clearHoldTimer();
    disposeDecoder();
    stopLevelAnimation();
    showOverlay.value = false;
    showCanvas.value = false;
    currentPlayer.value = null;
    animatedLevel.value = null;
    pendingLevelAnimation.value = null;
    void startNextUpgrade();
};

const scheduleHold = () => {
    if (typeof window === 'undefined') {
        handleUpgradeComplete();
        return;
    }
    holdTimer = window.setTimeout(() => {
        holdTimer = null;
        handleUpgradeComplete();
    }, HOLD_AFTER_FINAL_FRAME);
};

const drawFrame = async (frameIndex: number, token: number) => {
    if (!decoder || !upgradeCanvasRef.value || token !== renderToken) {
        return;
    }

    try {
        const { image } = await decoder.decode({ frameIndex });
        const canvas = upgradeCanvasRef.value;
        const ctx = canvas.getContext('2d');
        if (!ctx) {
            image.close();
            return;
        }

        if (canvas.width !== image.displayWidth || canvas.height !== image.displayHeight) {
            canvas.width = image.displayWidth;
            canvas.height = image.displayHeight;
        }

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(image, 0, 0, canvas.width, canvas.height);

        if (frameIndex >= OVERLAY_TRIGGER_FRAME && !showOverlay.value) {
            showOverlay.value = true;
            startPendingLevelAnimation(true);
        }

        const durationMicro = image.duration ?? 0;
        const durationMs = durationMicro > 0 ? durationMicro / 1000 : 40;
        const frameCount = decoder.tracks.selectedTrack?.frameCount ?? 1;
        const lastFrameIndex = frameCount - 1;
        image.close();

        if (frameIndex < lastFrameIndex) {
            if (typeof window === 'undefined') {
                void drawFrame(frameIndex + 1, token);
                return;
            }
            frameTimer = window.setTimeout(() => {
                void drawFrame(frameIndex + 1, token);
            }, durationMs);
        } else {
            scheduleHold();
        }
    } catch (error) {
        console.error('[Upgrade] 渲染 GIF 帧失败:', error);
        showCanvas.value = false;
        showOverlay.value = true;
        scheduleHold();
    }
};

const startDecoderForCurrentEffect = async () => {
    disposeDecoder();
    if (!currentPlayer.value) return;

    if (typeof window === 'undefined' || !('ImageDecoder' in window)) {
        console.error('[Upgrade] 当前环境不支持 ImageDecoder，使用静态展示');
        showCanvas.value = false;
        showOverlay.value = true;
        startPendingLevelAnimation(true);
        scheduleHold();
        return;
    }

    try {
        const response = await fetch(`${currentUpgradeGif.value}?v=${Date.now()}`);
        const arrayBuffer = await response.arrayBuffer();
        decoder = new ImageDecoder({ data: arrayBuffer, type: 'image/gif' });
        renderToken += 1;
        await drawFrame(0, renderToken);
    } catch (error) {
        console.error('[Upgrade] 初始化 ImageDecoder 失败:', error);
        showCanvas.value = false;
        showOverlay.value = true;
        startPendingLevelAnimation(true);
        scheduleHold();
    }
};

const startNextUpgrade = async () => {
    if (!upgradeQueue.value.length) {
        return;
    }
    const next = upgradeQueue.value.shift();
    if (!next) {
        return;
    }
    currentPlayer.value = next;
    const effectBaseLevel = next.effectLevel ?? next.newLevel;
    const effectConfig = getUpgradeEffectConfigByLevel(effectBaseLevel);
    currentEffectIndex.value = effectConfig.effectIndex;
    currentEffectTop.value = effectConfig.top;
    const fromLevel = next.oldLevel ?? next.newLevel;
    animatedLevel.value = fromLevel;
    pendingLevelAnimation.value = { from: fromLevel, to: next.newLevel };
    showOverlay.value = false;
    showCanvas.value = true;
    await nextTick();
    await startDecoderForCurrentEffect();
};

const upgradeAwards = ref<any[]>([]);
const upgradeAwardGifts = ref<any[]>([]);

function resolveAwardLookupLevel(level?: number | null): number | null {
    if (!Number.isFinite(level ?? NaN)) {
        return null;
    }
    const numericLevel = Number(level);
    if (numericLevel <= 0) {
        return null;
    }
    let divisor = 5;
    if (numericLevel > 100) {
        return -1;
    } else if (numericLevel >= 80 && numericLevel < 100) {
        divisor = 2;
    }
    const normalized = Math.floor(numericLevel / divisor) * divisor;
    if (normalized <= 0) {
        return null;
    }
    return normalized;
}

function findUpgradeAwardByLevel(level: number) {
    if (!Number.isFinite(level)) {
        return null;
    }
    for (const award of upgradeAwards.value) {
        if (!award) {
            continue;
        }
        const awardLevel = Number(award.level);
        if (!Number.isFinite(awardLevel)) {
            continue;
        }
        if (awardLevel === level) {
            return award;
        }
    }
    return null;
}

const sendAwardGiftForLevel = (playerData: Record<string, any>, level: number) => {
    if (!playerData || !Number.isFinite(level)) {
        return;
    }
    const lookupLevel = resolveAwardLookupLevel(level);
    if (!lookupLevel) {
        return;
    }
    const matchedAward = findUpgradeAwardByLevel(lookupLevel);
    if (!matchedAward) {
        return;
    }
    console.info('[Upgrade] 命中升级奖励', {
        level: lookupLevel,
        award: matchedAward,
        player: playerData
    });
    const normalizedPlatformId = String(playerData.userid_platform ?? '');
    const normalizedGiftName = String(matchedAward.giftName ?? '');
    const normalizedGiftCount = Number(matchedAward.gift_num) || 1;
    const normalizedGiftId = Number(matchedAward.gift_id) || 0;
    const nickname = playerData.playerName ?? playerData.player_name ?? '';
    const isLikeGift = normalizedGiftName === '点赞' || normalizedGiftName.toLowerCase() === 'like';
    const payload = isLikeGift
        ? {
            type: 'awardGift',
            Type: 2,
            Data: {
                User: {
                    Nickname: nickname
                },
                LikeCount: normalizedGiftCount
            }
        }
        : {
            type: 'awardGift',
            Type: 5,
            Data: {
                User: {
                    Nickname: nickname,
                    DisplayId: normalizedPlatformId,
                    Avatar: playerData.avatar
                },
                GiftName: normalizedGiftName,
                GiftCount: normalizedGiftCount,
                GiftId: normalizedGiftId,
                Level: lookupLevel,
                UpgradeGift: matchedAward.upgrade_gift
            }
        };
    sendMessage(payload);
};

const handlePlayerSeasonGift = (payload: PlayerSeasonEventPayload | null) => {
    if (!payload || !payload.player) {
        return;
    }
    const seasonPoints = Number(payload.seasonPoints ?? payload.player.season_points ?? 0);
    if (!Number.isFinite(seasonPoints) || seasonPoints <= 0) {
        return;
    }
    const level = getRank(seasonPoints);
    sendAwardGiftForLevel(payload.player, level);
};

const resetUpgradeAwards = () => {
    upgradeAwards.value = [];
    upgradeAwardGifts.value = [];
};

const fetchUpgradeAwards = async (gameId?: number | null, platformId?: number | string | null) => {
    if (!gameId || gameId <= 0 || platformId === undefined || platformId === null || platformId === '') {
        resetUpgradeAwards();
        return;
    }
    const parsedPlatform = Number(platformId);
    if (!Number.isFinite(parsedPlatform)) {
        resetUpgradeAwards();
        return;
    }
    try {
        const response = await getApi.getUpgradeAwards({
            game_id: gameId,
            platform: parsedPlatform
        });
        const data = response?.data || {};
        upgradeAwards.value = Array.isArray(data.awards) ? data.awards : [];
        upgradeAwardGifts.value = Array.isArray(data.gifts) ? data.gifts : [];
    } catch (error) {
        console.error('[Upgrade] 获取升级奖励失败:', error);
        resetUpgradeAwards();
    }
};

const enqueueUpgrades = (payload: UpgradeEventItem[]) => {
    if (!payload.length) {
        return;
    }
    upgradeQueue.value = [...upgradeQueue.value, ...payload];
    if (!currentPlayer.value) {
        void startNextUpgrade();
    }
};

const handleUpgrade = (message: UpgradeEventItem[] | UpgradeEventItem) => {
    if (!props.upgradeSwitch) {
        return;
    }
    const list = Array.isArray(message) ? message : [message];
    enqueueUpgrades(list);
};

watch([() => props.gameId, () => props.platformId], ([gameId, platformId]) => {
    if (!gameId || gameId <= 0 || platformId === undefined || platformId === null || platformId === '') {
        resetUpgradeAwards();
        return;
    }
    void fetchUpgradeAwards(Number(gameId), platformId);
}, { immediate: true });

onMounted(() => {
    eventBus.on('upgrade', handleUpgrade);
    eventBus.on('player-season-level-ready', handlePlayerSeasonGift);
});

onBeforeUnmount(() => {
    eventBus.off('upgrade', handleUpgrade);
    eventBus.off('player-season-level-ready', handlePlayerSeasonGift);
    clearHoldTimer();
    disposeDecoder();
    stopLevelAnimation();
});
</script>

<style scoped lang="less">
.upgrade {
    height: 300px;
    width: 300px;
    .box {
        position: relative;
        .player-name {
            position: absolute;
            top: 6%;
            left: 48%;
            transform: translateX(-50%);
            font-size: 17px;
            text-align: center;
            font-family: 'Square721BT';
            color: aliceblue;  
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 
                        1px 1px 2px rgba(0, 0, 0, 0.6),
                        0 0 8px rgba(255, 255, 255, 0.3);
            filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.5));
        }
        .upgrade-gif {
            height: 300px;
            width: 300px;
            object-fit: contain;
        }
        .default-avatar {
            position: absolute;
            top: 191px;
            left: 105px;
            height: 80px;
            width: 80px;
            clip-path: polygon(0% 0%, 100% 0%, 100% 80%, 50% 100%, 0% 80%);
            mask-image: radial-gradient(circle, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%);
            -webkit-mask-image: radial-gradient(circle, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%);
        }
        .upgrade-png {
            position: absolute;
            width: calc(186px * 0.25);
            height: calc(206px * 0.25);
            left: 121px;
            background-size: 100%;
            background-repeat: no-repeat;
            font-size: 18px;
            letter-spacing: -2px;
            font-family: 'Square721BT';  
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 
                        1px 1px 2px rgba(0, 0, 0, 0.6),
                        0 0 8px rgba(255, 255, 255, 0.3);
            filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.5));          
            color: aliceblue;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .player-rank {
            position: absolute;
            top: 247px;
            left: 48%;
            transform: translateX(-50%);
            font-size: 12px;
            text-align: center;
            font-family: 'Square721BT';
            color: aliceblue;  
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 
                        1px 1px 2px rgba(0, 0, 0, 0.6),
                        0 0 8px rgba(255, 255, 255, 0.3);
            filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.5));
        }
        .upgrade-txt {
            position: absolute;
            bottom: -6%;
            left: 48%;
            transform: translateX(-50%);
            font-size: 17px;
            text-align: center;
            font-family: 'Square721BT';
            color: aliceblue;  
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8), 
                        1px 1px 2px rgba(0, 0, 0, 0.6),
                        0 0 8px rgba(255, 255, 255, 0.3);
            filter: drop-shadow(1px 1px 2px rgba(0, 0, 0, 0.5));
        }
    }
}
</style>
